package com.wei.czz.framework.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.index.validateCode.ValidateCodeStatusEnum;
import com.wei.czz.common.enums.index.validateCode.ValidateCodeTypeEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.po.ValidateCodePo;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.framework.common.dao.ValidateCodeDao;
import com.wei.czz.framework.common.entity.ValidateCodeEntity;
import com.wei.czz.framework.common.handler.redis.RedisStringHandler;
import com.wei.czz.framework.common.service.ValidateCodeService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2022-04-27 11:26:00
 * className: UserCaptchaServiceImpl 用户验证码服务接口实现类
 * version: 1.0
 * description:
 */
@Service("validateCodeService")
@AllArgsConstructor
public class ValidateCodeServiceImpl extends ServiceImpl<ValidateCodeDao, ValidateCodeEntity>
        implements ValidateCodeService {

    private static final Logger log = LoggerFactory.getLogger(ValidateCodeServiceImpl.class);

    private final RedisStringHandler redisStringHandler;

    @Override
    public void saveOrEdit(ValidateCodeEntity validateCode) {
        Long id = validateCode.getId();
        if (Objects.isNull(id)) {

            int count = baseMapper.insert(validateCode);
            log.info("插入验证码完成。count={}", count);
        } else {

            int count = baseMapper.updateById(validateCode);
            log.info("修改验证码完成。count={}", count);
        }
    }

    @Override
    public ValidateCodeEntity get(Long id) {
        ValidateCodeEntity validateCode = baseMapper.selectById(id);
        if (Objects.isNull(validateCode)) {
            log.info("验证码不存在");
            throw new CzzException(ResultEnum.ILLEGAL_FAIL.getCode(), "验证码不存在，请确认");
        }
        if (CommonEnum.ONE.getValue().equals(validateCode.getDeleteStatus())) {
            log.info("验证码已删除");
            throw new CzzException(ResultEnum.ILLEGAL_FAIL.getCode(), "验证码不存在，请确认");
        }
        return validateCode;
    }

    @Override
    public Date getLastCreateTime(String account, Integer validateType) {
        return baseMapper.getLastCreateTime(account, validateType);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public void checkValidateCode(String account, String requestIp, ValidateCodeTypeEnum type, String code) {

        long now = System.currentTimeMillis();
        Date date = new Date(now);

        Long optUserId = CommonEnum.ZERO.getLongValue();
        String optUserName = StringUtils.EMPTY;
        try {
            UserPo userPo = SecurityUtils.getUser();

            optUserId = userPo.getUserId();
            optUserName = userPo.getUsername();
            log.info("校验验证码，操作用户：userId={} username={}", optUserId, optUserName);
        } catch (Exception e) {
            log.info("登录用户不存在。message={}", e.getMessage());
        }

        /*
            获取验证码缓存
         */
        String cacheKey = CacheKeyUtils.getValidateCodeKey(account, type.getValue());
        ValidateCodePo validateCodePo = redisStringHandler.get(cacheKey);
        if (Objects.isNull(validateCodePo)) {
            /*
                获取验证码
             */
            LambdaQueryWrapper<ValidateCodeEntity> validateCodeLambdaQueryWrapper = new LambdaQueryWrapper<>();
            validateCodeLambdaQueryWrapper.eq(ValidateCodeEntity::getAccount, account)
                    .eq(ValidateCodeEntity::getType, type.getValue())
                    .gt(ValidateCodeEntity::getExpireTime, now)
                    .eq(ValidateCodeEntity::getDeleteStatus, CommonEnum.ZERO.getValue());
            // 查询
            List<ValidateCodeEntity> validateCodeList = baseMapper.selectList(validateCodeLambdaQueryWrapper);
            log.info("查询验证码完成。account={} validateType={} size={}", account, type.getValue(),
                    validateCodeList.size());
            if (validateCodeList.isEmpty()) {
                log.info("找不到{}验证码", type.getName());
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "请获取验证码后在进行操作");
            }
            // 获取最后一条数据，查询结果已经默认按照主键升序排序，最后一条即最新一条验证码
            ValidateCodeEntity validateCode = validateCodeList.get(validateCodeList.size() - 1);
            log.info("{}验证码。{}", type.getName(), validateCode);

            /*
                校验验证码
             */
            // 判断验证码是否验证过
            if (!CommonEnum.ZERO.getValue().equals(validateCode.getAuditStatus())) {
                log.info("{}验证码已验证过", type.getName());
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "验证码已使用，请重新操作");
            }

            validateCodePo = new ValidateCodePo();
            validateCodePo.setId(validateCode.getId())
                    .setRequestIp(validateCode.getRequestIp())
                    .setCode(validateCode.getCode())
                    .setExpireTime(validateCode.getExpireTime());
        }
        if (!Objects.equals(validateCodePo.getRequestIp(), requestIp)) {
            log.warn("生成验证码ip和验证ip不一致。db.requestIp={} param.requestIp={}", validateCodePo.getRequestIp(),
                    requestIp);
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }
        // 判断验证码是否过期
        if (now > validateCodePo.getExpireTime()) {
            log.info("{}验证码过期", type.getName());
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "验证码已过期，请重新操作");
        }

        // 判断验证码是否正确
        boolean bool = Objects.equals(code, validateCodePo.getCode());
        if (bool) {
            // 验证成功
            ValidateCodeEntity validateCode = new ValidateCodeEntity();
            validateCode.setId(validateCodePo.getId())
                    .setAuditTime(now)
                    .setAuditStatus(ValidateCodeStatusEnum.SUCCESS_AUTH.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue());
            // 验证通过
            int count = baseMapper.updateById(validateCode);
            log.info("验证成功，修改验证码完成。count={}", count);
        } else {
            // 验证失败

            /*
                更新验证码的验证失败次数
             */
            int count = baseMapper.incAuditFailNum(account, type.getValue(), type.getMaxFailNum(), optUserId,
                    optUserName, date, CommonEnum.ZERO.getValue());
            log.info("修改验证码验证失败次数完成。count={}", count);
            if (count <= 0) {
                ValidateCodeEntity validateCode = new ValidateCodeEntity();
                validateCode.setId(validateCodePo.getId())
                        .setAuditTime(now)
                        .setAuditStatus(ValidateCodeStatusEnum.FAIL_AUTH.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(StringUtils.EMPTY)
                        .setUpdateUserId(CommonEnum.ZERO.getLongValue());
                count = baseMapper.updateById(validateCode);
                log.info("验证失败，修改验证码完成。count={}", count);
            }
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "验证码输入错误，请重新操作");
        }
    }

}